<template>
  <div class="fall-container" ref="container">
    <section :style="fallStyle">
      <article-card
        v-for="article of lists"
        :key="article.id"
        ref="card"
        :style="cardStyle"
        :width="`${cardWidth}px`"
        :article />
    </section>
  </div>
  <NavSelect class="nav-select" :list="categoryList" @select="changeNav" />
  <section id="loadingRef">
    <loading v-if="isLoading" class="fall-loading" />
    <p v-if="!isLoading && total === lists.length" class="loading-text">没有更多了</p>
  </section>
</template>

<script setup lang="ts">
import articleApi from '@/apis/article';
import type { ArticleApiType } from '@/apis/article/namespace';
import categoryApi from '@/apis/category';
import { debounceFunc } from '@/hooks/useDebounce';
import { useTemplateRef } from 'vue';
import ArticleCard from './article-card.vue';

// 文章列表
const lists = ref<ArticleApiType.Article[]>([]);

// 文章总数
const total = ref(0);

// 请求数据
const req = {
  page: 0,
  row: 20,
  categoryName: '',
};

// 容器实例
const containerRef = useTemplateRef<HTMLInputElement>('container');

// 栏目列表
const categoryList = ref<{ label: string; value: string }[]>([]);

// 文章卡片宽度
const cardWidth = 300;

// 卡片间距
const cardSpace = 20;

// 容器样式
const fallStyle = ref();

// 卡片样式
const cardStyle = ref();

// loading状态
const isLoading = ref(false);

/**
 * 获取栏目列表
 */
const getCategoryList = async () => {
  const res = await categoryApi.list();
  categoryList.value = res.map((item) => {
    return {
      label: item.title,
      value: item.title,
    };
  });
  categoryList.value.unshift({ label: '全部', value: '' });
};

/**
 * 切换栏目
 */
const changeNav = (val: { label: string; value: string }) => {
  lists.value = [];
  req.page = 0;
  req.categoryName = val.value;
  getArticles();
};

/**
 * 计算当前容器可展示列t
 */
const cal = (cardWidth: number) => {
  const containerWidth = containerRef.value?.clientWidth ?? 1024;

  // 计算列数量
  const columns = Math.floor((containerWidth + cardSpace) / (cardWidth + cardSpace));
  return columns;
};

/**
 * 更新布局样式
 */
const setStyle = () => {
  const columns = cal(cardWidth);
  cardStyle.value = { 'margin-bottom': `${cardSpace}px` };
  fallStyle.value = {
    'column-count': columns,
    'column-gap': `${cardSpace}px`,
  };
};

/**
 * 获取文章列表数据，同时更新样式
 */
const getArticles = async () => {
  try {
    isLoading.value = true;
    req.page++;

    // 获取文章列表
    const res = await articleApi.list(req);
    total.value = +res.total;

    if (res.list.length > 0) {
      lists.value.push(...res.list);
      setStyle();
    } else {
      req.page--;
    }
  } catch (error) {
    ElNotification({
      title: '错误',
      message: '获取文章列表失败',
      type: 'error',
    });
  } finally {
    isLoading.value = false;
  }
};

/**
 * 重置窗口
 */
const onResize = () => {
  window.addEventListener('resize', debounceFunc(setStyle, 300));
};

/**
 * 监控loading节点是否可见，如果可见，则触发获取文章列表数据
 */
let ob: IntersectionObserver;
onMounted(() => {
  ob = new IntersectionObserver(
    (entries) => {
      const entry = entries[0];
      if (isLoading.value) return;
      if (entry.isIntersecting) {
        getArticles();
      }
    },
    {
      threshold: 0,
    },
  );
  const loadingRef = document.querySelector('#loadingRef');
  loadingRef && ob.observe(loadingRef);
});

onUnmounted(() => {
  ob.disconnect();
});

onMounted(() => {
  getArticles();
  onResize();
  getCategoryList();
});
</script>
<style scoped lang="scss">
.fall-container {
  position: relative;
  display: flex;
  justify-content: center;
  padding: 1.25rem 0;
}
.nav-select {
  position: fixed;
  bottom: 2rem;
  right: 2rem;
}
.fall-loading {
  width: 10rem;
  height: 3rem;
  margin: 0 auto;
}
.loading-text {
  margin-top: 0.5rem;
  text-align: center;
  font-size: 1rem;
  color: #999;
}
</style>
